﻿/*
Настройки приложения: 
    connectionString - Строка подключения к БД
    activeDirectory - Сервер AD для подключения
    group - Группа AD
    exportPath - Директория для экспорта

Настройки запроса: 
    period - Период сбора данных
    ignoreUsers - Игнорируемые пользователи
    querryPath - путь к sql запросу в БД


Функционал:
    1. Получение пользователей AD
    2. Получение DataTable из DB
    3. Добавление к DataTable колонки Company
    4. Экспорт в .csv 
*/

using System.DirectoryServices;
using NLog;
using Newtonsoft.Json;
using System.Data;
using Microsoft.Data.SqlClient;
using System.Reflection;
using Aspose.Cells;


namespace ServiceStatistic
{
    class Config
    {
        public Dictionary<string, string> appParams;
        public Dictionary<string, dynamic> sqlQuerryparams;

        public Config(Dictionary<string, string> appParams, Dictionary<string, dynamic> sqlQuerryparams)
        {
            this.appParams = appParams;
            this.sqlQuerryparams = sqlQuerryparams;
        }
    }
    class ADResult
    {
        public string Path;
        public Dictionary<string, List<string>> Properties;

        public ADResult(string Path, Dictionary<string, List<string>> Properties)
        {
            this.Path = Path;
            this.Properties = Properties;
        }

    }

    class Program
    {
        public Config appConfig { get { return JsonConvert.DeserializeObject<Config>(GetConfig()); } }
        public List<ADResult> adUserList { get { return GetAdUserList(appConfig); } }

        protected static string CurrentDirectory { get { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } }
        protected static Logger _logger = LogManager.GetCurrentClassLogger();

        private string GetConfig()
        {
            string path = CurrentDirectory + "/appsetting.json";

            try
            {
                using (StreamReader reader = File.OpenText(path))
                {
                    return File.ReadAllText(path);
                }

                _logger.Info($"Найден конфигурационный файл {path}");
                System.Console.WriteLine("INFO ----> Найден конфигурационный файл {0}", path);
            }
            catch (System.Exception err)
            {
                _logger.Error($"Конфигурационный файл не найден." + err + "");
                System.Console.WriteLine("INFO ----> Конфигурационный файл не найден. " + err + "");

                return null;
            }

            return null;
        }

        private ADResult GetADUser(Config config, string userLogin)
        {
            DirectorySearcher searcher;
            SearchResultCollection searchResultCollection = null;

            try
            {
                using (DirectoryEntry directoryEntry = new DirectoryEntry(config.appParams["activeDirectory"]))
                {
                    searcher = new DirectorySearcher(directoryEntry)
                    {
                        Filter = "(&(objectclass=user)(SamAccountName=" + userLogin + "))"
                    };

                    searcher.PropertiesToLoad.Add("Name");
                    searcher.PropertiesToLoad.Add("Company");
                    searcher.PropertiesToLoad.Add("SamAccountName");

                    searchResultCollection = searcher.FindAll();

                    return JsonConvert.DeserializeObject<List<ADResult>>(JsonConvert.SerializeObject(searchResultCollection)).First();
                }
            }
            catch
            {
                System.Console.WriteLine($"WARN ----> Ошибка при получении пользователя | {userLogin} | из ActiveDirectory");
                _logger.Warn($"Ошибка при получении пользователя | {userLogin} | из ActiveDirectory");

                return null;
            }
        }

        private List<ADResult> GetAdUserList(Config config)
        {
            DirectorySearcher searcher;
            SearchResultCollection result = null;

            List<ADResult> AdUserList = new List<ADResult>();

            try
            {
                using (DirectoryEntry directoryEntry = new DirectoryEntry(config.appParams["activeDirectory"]))
                {
                    System.Console.WriteLine("INFO ----> Подключение к Active Directory...");
                    _logger.Info("Подключение к Active Directory...");

                    searcher = new DirectorySearcher(directoryEntry)
                    {
                        Filter = "(&(objectclass=user)(memberOf=" + config.appParams["group"] + "))"
                    };

                    searcher.PropertiesToLoad.Add("Name");
                    searcher.PropertiesToLoad.Add("Company");
                    searcher.PropertiesToLoad.Add("SamAccountName");

                    result = searcher.FindAll();
                    int ADusersNum = result.Count;

                    System.Console.WriteLine($"INFO ----> Найдено пользователей в AD: {ADusersNum}");
                    _logger.Info($"Найдено пользователей в AD: {ADusersNum}");

                    AdUserList = JsonConvert.DeserializeObject<List<ADResult>>(JsonConvert.SerializeObject(result));

                    _logger.Info($"Собрано {AdUserList.Count} из {ADusersNum - 1}");
                    System.Console.WriteLine($"INFO ----> Собрано {AdUserList.Count} из {ADusersNum}");
                }
            }
            catch (System.Exception err)
            {
                System.Console.WriteLine("ERROR ----> Ошибка при получении пользователей ActiveDirectory");
                _logger.Error($"Ошибка при получении пользователей ActiveDirectory\n{err}");
            }

            return AdUserList;
        }

        private string CreatCommad(Config config)
        {
            string result = string.Empty;

            try
            {
                string path = config.sqlQuerryparams["querryPath"];

                using (StreamReader reader = File.OpenText(path))
                {
                    result = File.ReadAllText(path);
                }
            }
            catch (System.Exception err)
            {
                System.Console.WriteLine($"ERROR ----> Ошибка при получении SQL-запроса");
                _logger.Error($"Ошибка при получении SQL-запроса\n{err}");
            }

            foreach (KeyValuePair<string, dynamic> item in config.sqlQuerryparams)
            {
                result = result.Replace("{" + item.Key + "}", (string)item.Value);
            }

            System.Console.WriteLine("INFO ----> Создан SQL-запрос");
            _logger.Trace($"INFO ----> Создан SQL-запрос\n{result}");

            return result;
        }

        public DataTable GetDataTable(Config config)
        {
            DataTable result = new DataTable();

            string connectionString = config.appParams["connectionString"];
            string command = CreatCommad(config);
            List<ADResult> adUserList = GetAdUserList(config);

            try
            {
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    System.Console.WriteLine($"INFO ----> Подключаюсь по строке подключения: {connectionString}...");
                    _logger.Info($"Подключаюсь по строке подключения: {connectionString}...");

                    using (SqlCommand cmd = connection.CreateCommand())
                    {
                        cmd.CommandText = command;
                        cmd.CommandType = CommandType.Text;
                        using (SqlDataAdapter dataAdapter = new SqlDataAdapter(cmd))
                        {
                            dataAdapter.Fill(result);
                        }
                    }
                    _logger.Info("Получена информация из БД. Число объектов " + result.Rows.Count + "");
                    System.Console.WriteLine("INFO ----> Получена информация из БД. Число объектов " + result.Rows.Count + "");
                }
            }
            catch (System.Exception err)
            {
                System.Console.WriteLine("ERROR ----> Не удалось подключиться к Базе Данных");
                _logger.Error(err);
            }

            DataColumn columnCompany = new DataColumn();
            columnCompany.ColumnName = "Company";
            columnCompany.DataType = System.Type.GetType("System.String");
            result.Columns.Add(columnCompany);

            System.Console.WriteLine("INFO ----> Собираю информацию об организации пользователей...");
            _logger.Info("Собираю информацию об организации пользователей...");

            foreach (DataRow row in result.Rows)
            {
                try
                {
                    string currentCompany = adUserList.First(user => user.Properties["samaccountname"].First().ToLower() == row["userName"].ToString().ToLower()).Properties["company"].First();
                    row["Company"] = currentCompany;
                }
                catch
                {
                    try
                    {
                        row["Company"] = GetADUser(config, row["userName"].ToString().ToLower()).Properties["company"].First();
                    }
                    catch
                    {
                        row["Company"] = null;
                    }
                }
            }

            System.Console.WriteLine($"INFO ----> Собрано строк данных: {result.Rows.Count}");
            return result;
        }

        public void ExportData(DataTable dataTable, Config config)
        {
            System.Console.WriteLine("INFO ----> Начинается экспорт данных...");
            _logger.Info("Начинается экспорт данных...");

            try
            {
                int index = 0;
                string filePath = string.Empty;
                string[] columnsName = dataTable.Columns.Cast<DataColumn>().Select(item => item.ColumnName).ToArray();

                switch (File.Exists(filePath))
                {
                    case true:
                        filePath = config.appParams["exportPath"] + @"\TMN_" + DateTime.Now.ToString("dd.MM.yy_HHmmss") + "_Users.xlsx";
                        break;
                    case false:
                        filePath = config.appParams["exportPath"] + @"\TMN_" + DateTime.Now.ToShortDateString() + "_Users.xlsx";
                        break;
                }

                Workbook workbook = new Workbook();
                Worksheet worksheet = workbook.Worksheets.Add("Users");

                foreach (string item in columnsName)
                {
                    worksheet.Cells[0, index].Value = item;
                    index++;
                }

                int activeCellRow = worksheet.Cells.LastCell.Row + 1;

                for (int row = 0; row < dataTable.Rows.Count; row++)
                {
                    for (int column = 0; column < dataTable.Columns.Count; column++)
                    {
                        worksheet.Cells[activeCellRow, column].Value = dataTable.Rows[row][column];
                    }

                    activeCellRow++;
                }

                worksheet.AutoFitColumns();
                workbook.Save(filePath, SaveFormat.Xlsx);

                System.Console.WriteLine("INFO ----> Экспорт завершен успешно");
                _logger.Info("Экспорт завершен успешно");
            }
            catch (System.Exception err)
            {
                System.Console.WriteLine("ERROR ----> Ошибка при попытке экспорта данных");
                _logger.Error($"Ошибка при попытке экспорта данных\n{err}");
            }
        }
    }

    class ServiceStatistic
    {
        static void Main()
        {
            System.Console.WriteLine("INFO ----> Начата работа программы...");

            Program program = new Program();
            Config config = program.appConfig;

            DataTable dataTable = program.GetDataTable(config);
            program.ExportData(dataTable, config);


            System.Console.WriteLine("INFO ----> Работа программы завершена");

        }
    }

}